#include "Basic/GxLibBasic.h"
#include "Windows/GxWinForm.h"
#include "Windows/GxWindows.h"
#include "Math.h"
#include "../RayTracePro/RayTracePro.h"
#include "../RayTracePro/BitmapPrinter.h"
#include "../RayTracePro/RenderThreadStorage.h"
#include "../RayTracePro/FastRandom.h"
#include "../RayTracePro/GI.h"
#include "../RayTracePro/Debug.h"
//#define DEBUG_SINGLE_THREAD 1

using namespace GxLibBasic;
using namespace GxWinGUI;
using namespace GxLibMath;

using namespace RayTracePro;

const int WindowWidth = 800;
const int WindowHeight = 600;

#define WM_UPDATE_IMAGE WM_USER+101

class MainForm : public Form
{
protected:
	volatile bool completed;
	int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
	{
		UINT  num = 0;          // number of image encoders
		UINT  size = 0;         // size of the image encoder array in bytes

		ImageCodecInfo* pImageCodecInfo = NULL;

		GetImageEncodersSize(&num, &size);
		if(size == 0)
			return -1;  // Failure

		pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
		if(pImageCodecInfo == NULL)
			return -1;  // Failure

		GetImageEncoders(num, size, pImageCodecInfo);

		for(UINT j = 0; j < num; ++j)
		{
			if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
			{
				*pClsid = pImageCodecInfo[j].Clsid;
				free(pImageCodecInfo);
				return j;  // Success
			}    
		}

		free(pImageCodecInfo);
		return -1;  // Failure
	}
	bool ProcessMessage(WinMessage msg)
	{
		bool rs = Form::ProcessMessage(msg);
		if (msg.message == WM_UPDATE_IMAGE)
		{
			printer.Flush();
			Graphics(this->handle).DrawImage(printer.GetBitmap(), 0, 0);
			if (completed)
			{
				CLSID clsid;
				GetEncoderClsid(L"image/bmp", &clsid);
				printer.GetBitmap()->Save(L"D:\\RenderResult.bmp", &clsid);
				buffer->SavePFM(L"D:\\RenderResult.pfm");
			}
			return true;
		}
		else
		{
			return rs;
		}
	}
private:
	Vec3 viewCenter;
	float viewTheta, viewPhi, viewRadius;
	bool onAdjustView;
	int lastX, lastY;
	RefPtr<Scene> scene;
	RefPtr<Renderer> renderer;
	RefPtr<RenderBuffer> buffer;
	RefPtr<RenderThreadScheduler> scheduler;
	RayTraceSystem * system;
	BitmapPrinter printer;
	LARGE_INTEGER startTime, lastUpdateTime;
	double invFreq;
	int threads;
	void InitRenderer()
	{
		system = CreateRayTraceSystem();
		buffer = new RenderBuffer();
		buffer->SetSize(WindowWidth, WindowHeight);
		renderer = system->CreateRenderer();
		renderer->SetPerspective(WindowWidth, WindowHeight, 50.0f);
		renderer->SetScreenBuffer(buffer.operator->());
		printer.SetResolution(WindowWidth, WindowHeight);
		for (int i = 0; i<WindowWidth; i++)
			for (int j = 0; j<WindowHeight; j++)
				printer.SetPixel(i, j, RayTracePro::Color(0,0,0));
		LARGE_INTEGER freq;
		QueryPerformanceFrequency(&freq);
		invFreq = 1.0 / (double)freq.QuadPart;

		viewTheta = viewPhi = 0.0f;
		viewRadius = 30.0f;
		viewCenter.SetZero();

#ifndef DEBUG_SINGLE_THREAD
		threads = DetectCpuCount();
		if (threads == 1) threads = 2;
#else
		threads = 1;
#endif
	}

	void UpdateViewTransform()
	{
		Matrix4 trans, trans2;
	
		Matrix4::RotationY(trans, viewTheta);
		Matrix4::RotationX(trans2, viewPhi);
		trans *= trans2;
		Vec3 dir(0.0f, 0.0f, -1.0f);
		Vec3 transDir;
		trans.Transform(transDir, dir);
		Vec3 pos = viewCenter;
		Vec3 dpos;
		Vec3::Scale(dpos, transDir, -1.0f * viewRadius);
		pos += dpos;
		Matrix4::Translation(trans2, pos.x, pos.y, pos.z);
		trans.LeftMultiply(trans2);
		renderer->Camera.CameraTransform = trans;
	}

	void InitScene0()
	{
		scene = system->CreateScene();

		MeshGeometry * mesh = new MeshGeometry();
		mesh->Normals.Add(Vec3(0.0f, 0.0f, 1.0f));
		mesh->Normals.Add(Vec3(0.0f, -1.0f, 0.0f));
		mesh->TexCoords.Add(Vec2(0.0f, 0.0f));
		int norms[3] = {0,0,0};
		int texs[3] = {0,0,0};
		Vec3 verts[3] = {Vec3(-10.0f, 10.0f, 0.0f), Vec3(-10.0f, -10.0f, 0.0f), Vec3(10.0f, -10.0f, 0.0f)};

		mesh->AddFace(verts, norms, texs);
		verts[0] = Vec3(10.0f, -10.0f, 0.0f);
		verts[1] = Vec3(-10.0f, -10.0f, 0.0f);
		verts[2] = Vec3(10.0f, -10.0f, -10.0f);
		norms[0] = norms[1] = norms[2] = 1;
		mesh->AddFace(verts, norms, texs);
		mesh->UpdateAccelerateStructure();
		scene->AddGeometry(mesh);

		viewRadius = 40.0f;
		viewCenter.SetZero();
		UpdateViewTransform();
	}

	void InitScene1()
	{
		viewPhi = 0.0;
		viewTheta = PI/4;
		viewRadius = 50.0f;
		scene = system->CreateScene();
		system->EnableGI = false;
		renderer->SetMultiSample(4);
		scene->Environment = new EnvironmentShader(system, L"Media/skybox_sunset.jpg");
		system->TextureFilter = RayTraceSystem::TextureFilterAnisotropic;
		system->TextureFilterAnisotropicRate = 16;
		Material * stdMaterial = scene->CreateMaterial();
		stdMaterial->Shader = system->CreateShader(L"Texture");
		stdMaterial->Shader->SetParameter(L"File Name", L"Media/Chessboard.bmp");
		Matrix4 trans;
		MeshGeometry * mesh = new MeshGeometry();
		mesh->Material = stdMaterial;
		Matrix4::Translation(trans, 0.0f, 0.0f, -50.0f);
		mesh->SetTransformation(trans);
		mesh->Normals.Add(Vec3(0.0f, 1.0f, 0.0f));
		mesh->TexCoords.Add(Vec2(0.0f, 0.0f));
		mesh->TexCoords.Add(Vec2(0.0f, 100.0f));
		mesh->TexCoords.Add(Vec2(100.0f, 100.0f));
		mesh->TexCoords.Add(Vec2(100.0f, 0.0f));
		Vec3 verts[3];
		verts[0] = Vec3(-1000.0f, -11.0f, -1000.0f);
		verts[1] = Vec3(-1000.0f, -11.0f, 1000.0f);
		verts[2] = Vec3(1000.0f, -11.0f, 1000.0f);
		int norms[3] = {0,0,0};
		int texs[3] = {0, 1, 2};
		mesh->AddFace(verts, norms, texs);
		verts[0] = Vec3(-1000.0f, -10.0f, -1000.0f);
		verts[1] = Vec3(1000.0f, -10.0f, 1000.0f);
		verts[2] = Vec3(1000.0f, -10.0f, -1000.0f);
		int texs2[3] = {0, 2, 3};
		mesh->AddFace(verts, norms, texs2);
		mesh->UpdateAccelerateStructure();
		scene->AddGeometry(mesh);

		SphereGeometry * sphere = new SphereGeometry(10.0f);
		Material * reflectMaterial = scene->CreateMaterial();
		reflectMaterial->Shader = system->CreateShader(L"Fresnel");
		reflectMaterial->Shader->SetParameter(L"RefractionIndex", Vec4(1.4f));
		sphere->Material = reflectMaterial;
		scene->AddGeometry(sphere);

		viewCenter.SetZero();
		UpdateViewTransform();
	}

	void InitScene2()
	{
		//viewTheta = -3.473206f; viewPhi = 0.785398f;
		viewPhi = -0.1f;
		viewTheta = -1.832596f;
		viewRadius = 20.0f;
		viewCenter = Vec3(-1280.0f, 650.0f, -95.0f);
		scene = system->CreateScene();
		scene->Environment = new EnvironmentShader(system, L"Media/sky_midnoon.jpg");
		system->EnableGI = false;
		system->TextureFilter = RayTraceSystem::TextureFilterLinear;
		system->TotalPhotons = 120000;
		system->PhotonTraceDepth = 30;
		system->PhotonsPerEstimate = 250;
		system->FinalGatherRays = 64;
		system->EnableFinalGather = false;
		scene->GlobalPhotonMapRadianceModifier.Value = 80.0f;
		scene->GlobalPhotonMapRadianceModifier.Modifier = PhotonMapRadiusModifier::Set;
		Material * stdMaterial = scene->CreateMaterial();
		renderer->SetMultiSample(1);
		stdMaterial->Shader = system->CreateShader(L"Lambert");
		stdMaterial->Shader->SetParameter(L"Diffuse", Vec4(1.0f, 1.0f, 1.0f, 1.0f));
		system->ShadowBias = 0.02f;

		Light * light = scene->CreateLight();
		light->CastShadow = false;
		light->IsDirectional = true;
		light->Intensity = Vec3(5.0f, 5.0f, 5.0f);
		light->Direction = Vec3(2.3f, -10.0f, 2.0f);
		Vec3::Normalize(light->Direction, light->Direction);
		light->Position = Vec3(0.0f, 600.0f, 0.0f);
		system->AmbientLight = Vec3(0.0f, 0.0f, 0.0f);
		MeshGeometry * mesh = new MeshGeometry();

		GeometryLoader::Init();
		GeometryLoader::LoadGeometryFromFile(system, *mesh, L"Media/sponza.obj");
		GeometryLoader::Finalize();
		mesh->Material = stdMaterial;
		mesh->UpdateAccelerateStructure();
		scene->AddGeometry(mesh);
		UpdateViewTransform();
	}


	void InitScene3()
	{
		scene = system->CreateScene();
		scene->Environment = new EnvironmentShader(system, L"Media/DH046LL.jpg");
		Material * stdMaterial = scene->CreateMaterial();
		stdMaterial->Shader = system->CreateShader(L"Lambert");
		stdMaterial->Shader->SetParameter(L"Diffuse", Vec4(0.72f, 0.62f, 0.52f, 1.0f));
		stdMaterial->GI.Diffuse.Set(Vec4(0.72f, 0.62f, 0.52f, 1.0f));
		stdMaterial->GI.Lambert = true;
		Material * groundMaterial = scene->CreateMaterial();
		groundMaterial->Shader = system->CreateShader(L"Lambert");
		groundMaterial->Shader->SetParameter(L"Diffuse", Vec4(0.6f, 0.6f, 0.6f, 1.0f));
		groundMaterial->GI.Diffuse.Set(Vec4(0.6f, 0.6f, 0.6f, 1.0f));
		groundMaterial->GI.Lambert = true;
		Light * light = scene->CreateLight();
		light->CastShadow = true;
		light->IsDirectional = true;
		
		light->Direction = Vec3(-3.0f, -8.0f, -6.0f);
		Vec3::Normalize(light->Direction, light->Direction);
		light->Position = Vec3(300.0f, 600.0f, 300.0f);

		system->AmbientLight.SetZero();
		system->EnableFinalGather = true;
		system->EnableGI = true;
		system->TotalPhotons = 500000;
		renderer->SetMultiSample(8);
		MeshGeometry * mesh = new MeshGeometry();
		LoadSponge(*mesh, 4);
		mesh->Material = stdMaterial;
		mesh->UpdateAccelerateStructure();
		
		scene->AddGeometry(mesh);

		{
			MeshGeometry * mesh = new MeshGeometry();
			mesh->Material = groundMaterial;
			mesh->Normals.Add(Vec3(0.0f, 1.0f, 0.0f));
			mesh->TexCoords.Add(Vec2(0.0f, 0.0f));
			mesh->TexCoords.Add(Vec2(0.0f, 100.0f));
			mesh->TexCoords.Add(Vec2(100.0f, 100.0f));
			mesh->TexCoords.Add(Vec2(100.0f, 0.0f));
			Vec3 verts[3];
			verts[0] = Vec3(-10.0f, -1.0f, -10.0f);
			verts[1] = Vec3(-10.0f, -1.0f, 10.0f);
			verts[2] = Vec3(10.0f, -1.0f, 10.0f);
			int norms[3] = {0,0,0};
			int texs[3] = {0, 1, 2};
			mesh->AddFace(verts, norms, texs);
			verts[0] = Vec3(-10.0f, -1.0f, -10.0f);
			verts[1] = Vec3(10.0f, -1.0f, 10.0f);
			verts[2] = Vec3(10.0f, -1.0f, -10.0f);
			int texs2[3] = {0, 2, 3};
			mesh->AddFace(verts, norms, texs2);
			mesh->UpdateAccelerateStructure();
			scene->AddGeometry(mesh);
		}

		viewRadius = 4.2f;
		viewTheta = 0.610865f;
		viewPhi = 0.433972f;
		viewCenter.SetZero();
		viewCenter.y = -0.2f;
		UpdateViewTransform();
	}

	void InitScene4()
	{
		scene = system->CreateScene();

		Material * stdMaterial = scene->CreateMaterial();
		stdMaterial->Shader = system->CreateShader(L"Lambert");
		stdMaterial->GI.Lambert = true;
		stdMaterial->GI.Diffuse.Set(stdMaterial->Shader);
		stdMaterial->Shader->AddRef();
		stdMaterial->Shader->SetParameter(L"Diffuse", Vec4(1.0f, 1.0f, 1.0f, 1.0f));
		system->EnableGI = true;
		system->TotalPhotons = 500000;
		system->PhotonTraceDepth = 30;
		system->PhotonsPerEstimate = 200;
		system->EnableFinalGather = true;
		system->FinalGatherRays = 64;
		renderer->SetMultiSample(16);
		for (int i = 0; i<20; i++)
		{
			Light * light = scene->CreateLight();
			light->CastShadow = true;
			light->Decay = Light::Quadric;
			light->DecayFactor = 1.0f;
			light->Intensity = Vec3(3.0f, 3.0f, 3.0f);
			light->Position = Vec3(-12.0f + (float)i, 5.0f, -4.0f);
			light = scene->CreateLight();
			light->CastShadow = true;
			light->Decay = Light::Quadric;
			light->DecayFactor = 1.0f;
			light->Intensity = Vec3(4.0f, 4.0f, 4.0f);
			light->Position = Vec3(-12.0f + (float)i, 5.0f, 6.0f);
		}
		system->AmbientLight.SetZero();
		system->ShadowBias = 0.1f;
		MeshGeometry * mesh = new MeshGeometry();
		Matrix4 trans, rot;
		Matrix4::Translation(trans, -17.0f, -3.0f, 12.0f);
		Matrix4::RotationX(rot, PI/2);
		trans *= rot;
		mesh->SetTransformation(trans);
		mesh->AutoFlipNormals = true;
		/*Matrix4::Rotation(rot, Vec3(1.0f, 0.0f, 0.0f), -PI/12);
		trans *= rot;*/
		//Matrix4::Translation(trans, 28.0f, 2.0f, -2.0f);
		/*Matrix4::Rotation(rot, Vec3(0.0f, 1.0f, 0.0f), -PI/4-PI/24);
		trans *= rot;
		Matrix4::Rotation(rot, Vec3(1.0f, 0.0f, 0.0f), PI/12);
		trans *= rot;
		renderer->Camera.CameraTransform = trans;*/
		GeometryLoader::Init();
		GeometryLoader::LoadGeometryFromFile(system, *mesh, L"Media/conference.obj");
		GeometryLoader::Finalize();
		mesh->Material = stdMaterial;
		mesh->UpdateAccelerateStructure();
		scene->AddGeometry(mesh);

		viewRadius = 10.0f;
		viewTheta = 1.239184f; 
		viewPhi = 0.209440f;
		viewCenter.SetZero();
		UpdateViewTransform();
	}

	void InitScene5()
	{
		scene = system->CreateScene();
		scene->Environment = new EnvironmentShader(system, L"Media/DH046LL.jpg");
		Material * reflectMaterial = scene->CreateMaterial();
		reflectMaterial->Shader = system->CreateShader(L"Fresnel");
		reflectMaterial->Shader->SetParameter(L"RefractionIndex", Vec4(1.4f));

		Light * light = scene->CreateLight();
		light->CastShadow = false;
		light->Position = Vec3(300.0f, 600.0f, 300.0f);

		MeshGeometry * mesh = new MeshGeometry();
		Matrix4 trans, rot;
		Matrix4::Translation(trans, 0.0f, -8.0f, 0.0f);
		mesh->SetTransformation(trans);

		GeometryLoader::Init();
		GeometryLoader::LoadGeometryFromFile(system, *mesh, L"Media/bunny.obj");
		GeometryLoader::Finalize();
		mesh->Material = reflectMaterial;
		mesh->UpdateAccelerateStructure();

		scene->AddGeometry(mesh);

		viewRadius = 20.0f;
		viewTheta = -0.174533f; 
		viewPhi = 0.523599f;
		viewCenter.SetZero();
		UpdateViewTransform();
	}

	void MainForm_Paint(Object * sender, PaintEventArgs args)
	{
		//args.Graphics->Clear(Gdiplus::Color(0, 0, 0));
		args.Graphics->DrawImage(printer.GetBitmap(), 0, 0);
	}

	void TestPixel(int x, int y)
	{
		if (scene)
		{
			RenderThreadStorage storage(50);
			TlsSetValue(system->TlsId, &storage);
			DifferentialGeometry dgeom;
			Vec4 rs; rs.SetZero();
			system->DebugPrint = true;
			renderer->RenderPixel(rs, dgeom, x, y);
			printf("Debug render pixel (%d, %d) = (%.3f, %.3f, %.3f, %.3f)\n", x, y, rs.x, rs.y, rs.z, rs.w);
		
			printf("Intersect(%d): \n  normal(%f, %f, %f), pos(%f, %f, %f), tex(%f, %f)\n", dgeom.Geometry, dgeom.Normal.x, dgeom.Normal.y, dgeom.Normal.z,
				dgeom.Position.x, dgeom.Position.y, dgeom.Position.z, dgeom.UV.x, dgeom.UV.y);
			system->DebugPrint = false;
			rs = buffer->GetPixel(x, y);
			printf("buffer (%d, %d) = (%.3f, %.3f, %.3f, %.3f)\n", x, y, rs.x, rs.y, rs.z, rs.w);

		}
	}

	void MainForm_MouseDown(Object * sender, MouseEventArgs args)
	{
		if (renderer)
		{
			if (args.Button == GxWinGUI::mbRight)
			{
				TestPixel(args.X, args.Y);
			}
			else
			{
				onAdjustView = true;
				lastX = args.X;
				lastY = args.Y;
				SetCapture(this->GetHandle());
			}
		}
	}

	void MainForm_MouseMove(Object * sender, MouseEventArgs args)
	{
		if (onAdjustView)
		{
			viewPhi += (args.Y-lastY)/180.0f*PI;
			viewTheta += (lastX - args.X)/180.0f*PI;
			viewPhi = min(viewPhi, PI/2);
			viewPhi = max(viewPhi, -PI/2);
			lastX = args.X;
			lastY = args.Y;
			if (scheduler)
			{
				UpdateViewTransform();
				//memset(buffer->Buffer.Buffer(), 0, sizeof(Vec4)*buffer->Buffer.Count());
				QueryPerformanceCounter(&startTime);
				lastUpdateTime = startTime;
				scheduler->Render(threads);
				printf("ViewTheta = %f; ViewPhi = %f\n", viewTheta, viewPhi);
			}
		}
	}

	void MainForm_MouseUp(Object * sender, MouseEventArgs args)
	{
		onAdjustView = false;
		ReleaseCapture();
	}

	void MainForm_KeyPressed(Object * sender, KeyEventArgs args)
	{
		bool render = false;
		if (args.Key == L'0')
		{
			InitScene0();
			render = true;
		}
		else if (args.Key == L'1')
		{
			InitScene1();
			render = true;
		}
		else if (args.Key == L'2')
		{
			InitScene2();
			render = true;
		}
		else if (args.Key == L'3')
		{
			InitScene3();
			render = true;
		}
		else if (args.Key == L'4')
		{
			InitScene4();
			render = true;
		}
		else if (args.Key == L'5')
		{
			InitScene5();
			render = true;
		}
		else if (args.Key == VK_ESCAPE)
		{
			if (scheduler)
				scheduler->Cancel();
		}
		else if (args.Key == L'D' || args.Key == L'd')
		{
			TestPixel(264, 168);
		}
		else if (args.Key == L'X' || args.Key == L'x')
		{
			if (renderer)
			{
				renderer->DebugDrawPhotons();
				RefreshContent();
			}
		}
		if (render)
		{
			if (scheduler)
				scheduler->Cancel();
			completed = false;
			scheduler = new RenderThreadScheduler(renderer.operator->(), 64, 64);
			scheduler->RenderCompleted.Bind(this, &MainForm::RenderCompleted);
			scheduler->BlockCompleted.Bind(this, &MainForm::BlockCompleted);
			scheduler->BlockAssigned.Bind(this, &MainForm::BlockAssigned);
			renderer->SetScene(scene.operator->());
			scene->PrepareRendering(10);
			printf("Render started...\n");
			QueryPerformanceCounter(&startTime);
			lastUpdateTime = startTime;
			scheduler->Render(threads);
		}
	}

	void RefreshContent()
	{
		for (int i = 0; i < buffer->Height; i++)
		{
			for (int j = 0; j < buffer->Width; j++)
			{
				RayTracePro::Color c;
				Vec4 & pix = buffer->GetPixel(j, i);
				c.R = min(255, Round(pix.x * 255));
				c.G = min(255, Round(pix.y * 255));
				c.B = min(255, Round(pix.z * 255));
				c.A = 255;
				printer.SetPixel(j, i, c);
			}
		}
		printer.Flush();
		Graphics(this->handle).DrawImage(printer.GetBitmap(), 0, 0);
	}

	void BlockCompleted(const RenderTask& task)
	{
		for (int i = task.y; i < task.h + task.y; i++)
		{
			for (int j = task.x; j < task.w + task.x; j++)
			{
				RayTracePro::Color c;
				Vec4 & pix = buffer->GetPixel(j, i);
				c.R = min(255, Round(pix.x * 255));
				c.G = min(255, Round(pix.y * 255));
				c.B = min(255, Round(pix.z * 255));
				c.A = 255;
				printer.SetPixel(j, i, c);
			}
		}
	}

	void BlockAssigned(const RenderTask& task)
	{
		printer.DrawFocusRect(task.x, task.x + task.w - 1, task.y, task.y + task.h - 1);
		LARGE_INTEGER endTime;
		QueryPerformanceCounter(&endTime);
		if ((endTime.QuadPart - lastUpdateTime.QuadPart) * invFreq > 1.0)
		{
			PostMessage(this->handle, WM_UPDATE_IMAGE, 0, 0);
			lastUpdateTime = endTime;
		}
	}

	void RenderCompleted(bool success)
	{
		LARGE_INTEGER endTime;
		QueryPerformanceCounter(&endTime);
		
		double time = (endTime.QuadPart-startTime.QuadPart) * invFreq;
		if (success)
			printf("Render completed.\n");
		else
			printf("Render cancelled.\n");
		printf("Time elapsed: %.2fs\n", time);
		completed = true;
		printf("KD Traversal time: %fs\n", DebugTimming::GetTime(0));
		printf("   Primary triangle time: %fs\n", DebugTimming::GetTime(1));
		printf("   Shadow triangle time: %fs\n", DebugTimming::GetTime(2));
		PostMessage(this->handle, WM_UPDATE_IMAGE, 0, 0);
	}

public:
	MainForm()
	{
		this->SetMaximizeBox(false);
		this->SetText(L"RayTracePro");
		this->SetBorder(GxWinGUI::fbSingle);
		this->SetClientWidth(WindowWidth);
		this->SetClientHeight(WindowHeight);
		this->OnKeyPressed.Bind(this, &MainForm::MainForm_KeyPressed);
		this->OnPaint.Bind(this, &MainForm::MainForm_Paint);
		this->OnMouseDown.Bind(this, &MainForm::MainForm_MouseDown);
		this->OnMouseMove.Bind(this, &MainForm::MainForm_MouseMove);
		this->OnMouseUp.Bind(this, &MainForm::MainForm_MouseUp);
		InitRenderer();
		onAdjustView = false;
	}
	~MainForm()
	{
		scene = 0;
		renderer = 0;
		buffer = 0;
		scheduler = 0;
		FreeRayTraceSystem(system);
	}
};

int __stdcall _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) 
{
	Application::Init(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
	MainForm * form = new MainForm();
	Application::Run(form);
	Application::Dispose();
	return 0;
}

void TestPhotons()
{
	Photon p;
	Vec3 dir;
	DirectionLookupTable table;
	for (int i = 0; i<10000; i++)
	{
		dir.x = rand()/(float)RAND_MAX;
		dir.y = rand()/(float)RAND_MAX;
		dir.z = rand()/(float)RAND_MAX;
		Vec3::Normalize(dir, dir);
		p.SetDirection(dir);
		Vec3 dir2;
		p.GetDirection(dir2, table);
		float dist = acos(Vec3::Dot(dir2, dir));
		if (dist > PI/128.0f)
			printf("%f %f %f --- %f %f %f\n", dir.x, dir.y, dir.z, dir2.x, dir2.y, dir2.z);

		/*dir.x = rand()/(float)RAND_MAX * 1000.0f;
		dir.y = rand()/(float)RAND_MAX * 1000.0f;
		dir.z = rand()/(float)RAND_MAX * 1000.0f;
		p.SetPower(dir);
		Vec4 power2;
		p.GetPower(power2);
		power2.xyz(dir2);
		Vec3 tmp;
		Vec3::Subtract(tmp, dir, dir2);
		dist = Vec3::Dot(tmp, tmp);
		if (dist > Vec3::Dot(dir, dir) * 0.01f)
			printf("%f %f %f --- %f %f %f\n", dir.x, dir.y, dir.z, dir2.x, dir2.y, dir2.z);*/
	}
}

void TestRandom()
{
	int range[10];
	FastRandom rand(0);
	memset(range, 0, sizeof(int)*10);
	for (int i = 0; i<1000000; i++)
	{
		range[(int)(rand.NextFloat()*10)] ++;
	}
	for (int i = 0; i<10; i++)
	{
		printf("%d ", range[i]);
	}
	printf("\n");
}

int main()
{
	//TestPhotons();
	//TestRandom();
	printf("Press 1-5 to begin render\n");
	return _tWinMain((HINSTANCE)GetModuleHandle(NULL), NULL, GetCommandLine(), 0);
}
